package com.cskaoyan.javase.oop2._1extends._9hidden;

/**
 * 在上面章节中，我们已经知道，父子类的成员是允许同名的。
 * 那么如果父子类属性同名了，那么不使用super区分，直接访问的效果是什么样的呢？
 *
 * 现在我们直接创建子类对象，使用子类引用接收。
 * 会发现访问的结果是子类中属性的取值，那么父类中的那个成员变量去哪了呢？
 *
 * 首先它肯定是存在的，这一点随便写一个父类成员变量的Getter方法就可以验证了。
 * 这说明使用 "对象名点"访问成员变量这个语法，可能存在一些特殊的机制
 * 导致这里无法访问到父类的同名成员变量,这就是属性的隐藏
 * 接下来我们就来探讨这种机制。
 *
 * 在研究 对象名点访问成员变量 机制时，我们主要探究两个方向：
 *      1.访问范围，一个引用究竟能够访问到哪些成员变量？根据什么来决定？
 *      2.访问结果，如果是父子类同名成员变量，那么结果是什么？根据什么来决定？
 *
 * 在具体研究之前,我们还需要知道,在存在继承关系时,new创建对象的方式:
 *      1.父类引用指向父类对象
 *      2.子类引用指向子类对象
 *      3.父类引用指向子类对象(向上转型)
 *
 * 首先研究对象名点访问成员变量的,访问范围:
 *      1.父类引用指向父类对象
 *          引用是父类类型,对象也是父类类型
 *          访问范围就只有父类
 *
 *      2.子类引用指向子类对象
 *          引用是子类类型,对象也是子类类型
 *          访问范围是子类 + 父类
 *
 *      3.父类引用指向子类对象(向上转型)
 *          引用是父类类型,对象却是子类类型
 *          访问范围是父类
 *
 * 综上所述,对象名点访问成员变量,它的访问范围只需要看引用的数据类型就可以了.
 *      如果引用是父类类型,访问范围是父类
 *      如果引用是子类类型,访问范围是父类 + 子类
 *
 * 扩展:
 *      1.Father fs = new Son();
 *      这样一条new对象的语句,右边是创建对象
 *      但是对象必须用引用指向才能访问
 *      左边的创建引用就是该引用数据类型成员变量的声明
 *      声明的目的就是告诉编译器该对象的数据类型
 *
 *      访问范围是一个编译上的概念,由编译器决定
 *      能访问就不会编译报错,否则会报错
 *      上述语句,声明中指出了是一个Father对象
 *      所以访问范围就是Father类
 *
 *      2.当出现父类引用指向子类对象
 *          和子类引用指向子类对象
 *          区别是引用的访问范围变了
 *          子类引用访问范围是 父+子
 *          父类引用访问范围是 父
 *
 *      3.访问范围是一个编译概念
 *          编译器在查找一个成员变量能否访问时:
 *          首先去引用的类型中查找
 *          如果找不到就去它的父类中找
 *          直到找到Object类
 *          如果还找不到,说明这个成员不存在,编译报错
 *          可以往上找父类,但一定不可能向下找子类
 *
 *
 * 接下来研究对象名点访问成员变量的,访问结果:
 *          1.父类引用指向父类对象
 *              访问结果就看父类就行了
 *              和子类没有关系
 *
 *          2.子类引用指向子类对象
 *              访问结果
 *              如果子类中有该成员变量 结果就是子类的
 *              如果没有,那就是访问父类中的成员变量
 *              结果是父类中的结果
 *
 *          3.父类引用指向子类对象(向上转型)
 *              引用是父类类型
 *              对象却是子类类型
 *              访问父子类同名成员变量
 *              结果是父类中成员变量的结果
 *
 *  以上总结:
 *          对象名点访问成员变量
 *          访问结果,仍然只需要看引用就可以了
 *          引用的类型中该成员变量的结果是什么
 *          那么结果就是它
 *
 * 综合对象名点访问成员变量的范围和结果，发现：
 * 都是根据引用的数据类型来决定的，无需考虑是何种对象。(非常重要)
 *
 * @since 15:46
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        /*Son s = new Son();
        System.out.println(s.num);
        System.out.println(s.getNum());
        System.out.println(s.referenceSuperNum());*/

        // 1.父类引用指向父类对象
        Father f = new Father();
        System.out.println("f.fatherNum = " + f.fatherNum);
        System.out.println("f.num = " + f.num);

        // 2.子类引用指向子类对象
        Son s = new Son();
        System.out.println("s.fatherNum = " + s.fatherNum);
        System.out.println("s.num = " + s.num);
        System.out.println("s.sonNum = " + s.sonNum);

        // 3.父类引用指向子类对象(向上转型)
        Father fs = new Son();

        // System.out.println(((Son) fs).sonNum);
        // 如果不强转是访问不到子类独有成员变量的
        // fs.sonNum
        System.out.println("fs.fatherNum = " + fs.fatherNum);
        System.out.println("fs.num = " + fs.num);

    }
}

class Father {
    int num = 666;
    // 父类中独有的成员变量
    int fatherNum = 100;

    /*public int getNum() {
        return num;
    }*/
}

class Son extends Father {
    int num = 777;
    // 子类中独有的成员变量
    int sonNum = 200;
    /*public int referenceSuperNum(){
        return super.num;
    }*/
}